from utils.key import is_valid_redis_key, get_all_keys
from redisData.rServer import RS

# 存放sortedset的键值
zset_data = {}


def zsort(dict_tmp: dict) -> dict:
    new_dict = {}
    new_dict = dict(sorted(dict_tmp.items(), key=lambda item: item[1]))
    return new_dict


def zsortrever(dict_tmp: dict) -> dict:
    new_dict = {}
    new_dict = dict(sorted(dict_tmp.items(), key=lambda item: item[1], reverse=True))
    return new_dict


def check_zset_key(zset_key, rClient):
    all_keys = get_all_keys(rClient)
    if zset_key in all_keys:
        if all_keys[zset_key] != "sortedset":
            return "(error) WRONGTYPE Operation against a key holding the wrong kind of value"


# create
def zadd(cmd_list, rClient):
    zset_data = RS.rDB[rClient.redisDbId].zset_data
    cmd_list.pop(0)
    if len(cmd_list) >= 3 and len(cmd_list) % 2 == 1:
        all_keys = get_all_keys(rClient)
        key = cmd_list[0]
        if is_valid_redis_key(key):
            cmd_list.pop(0)
            check_zset_key(key, rClient)
            if key not in all_keys:
                tmp_zset = {}
                for i in range(0, len(cmd_list), 2):
                    try:
                        cmd_list[i] = float(cmd_list[i])
                    except:
                        return "the score is not float"
                    if i + 1 < len(cmd_list):
                        tmp_zset[cmd_list[i + 1]] = cmd_list[i]
                tmp_zset = zsort(tmp_zset)
                zset_data[key] = tmp_zset
                return "(integer)" + str(len(zset_data[key]))
            else:
                tmp_zset = zset_data[key]
                for i in range(0, len(cmd_list), 2):
                    try:
                        cmd_list[i] = float(cmd_list[i])
                    except:
                        return "the score is not float"
                    if i + 1 < len(cmd_list):
                        tmp_zset[cmd_list[i + 1]] = cmd_list[i]
                tmp_zset = zsort(tmp_zset)
                zset_data[key] = tmp_zset
                return "(integer)" + str(len(zset_data[key]))
        else:
            return "(error) Invalid key"
    else:
        return "(error) ERR wrong number of arguments for 'zadd' command "


# remove
def zrem(cmd_list, rClient):
    zset_data = RS.rDB[rClient.redisDbId].zset_data
    cmd_list.pop(0)
    if len(cmd_list) >= 2:
        all_keys = get_all_keys(rClient)
        key = cmd_list[0]
        if is_valid_redis_key(key):
            cmd_list.pop(0)
            check_zset_key(key, rClient)
            if key not in all_keys:
                return "(error)ERR the key is not existed"
            else:
                new_dict = {key: value for key, value in zset_data[key].items() if key not in cmd_list}
                zset_data[key] = new_dict
                return "(integer)" + str(len(zset_data[key]))
        else:
            return "(error)Invalid key"
    else:
        return "(error) ERR wrong number of arguments for 'zrem' command "


# 得出某元素的得分，不改变zset
def zscore(cmd_list, rClient):
    zset_data = RS.rDB[rClient.redisDbId].zset_data
    cmd_list.pop(0)
    if len(cmd_list) == 2:
        all_keys = get_all_keys(rClient)
        key = cmd_list[0]
        if is_valid_redis_key(key):
            cmd_list.pop(0)
            check_zset_key(key, rClient)
            if key not in all_keys:
                return "(error)ERR the key is not existed"
            else:
                if cmd_list[0] in zset_data[key]:
                    return str(key) + "score is " + str(zset_data[key][cmd_list[0]])
                else:
                    return "(error)There was no member named" + str(cmd_list[0])
        else:
            "(error)Invalid key"
    else:
        return "(error) ERR wrong number of arguments for 'zscore' command "


# 将元素添加偏移量并重新排列，改变zset
def zincrby(cmd_list, rClient):
    zset_data = RS.rDB[rClient.redisDbId].zset_data
    cmd_list.pop(0)
    if len(cmd_list) == 3:
        all_keys = get_all_keys(rClient)
        key = cmd_list[0]
        if is_valid_redis_key(key):
            cmd_list.pop(0)
            check_zset_key(key, rClient)
            if key not in all_keys:
                return "(error)ERR the key is not existed"
            else:
                try:
                    incr = float(cmd_list[0])
                except:
                    return "there was a wrong type about incredent"
                if cmd_list[1] in zset_data[key]:
                    zset_data[key][cmd_list[1]] += incr
                    zset_data[key] = zsort(zset_data[key])
                    return "Done"
                else:
                    return "(error)There was no member named" + str(cmd_list[1])
        else:
            return "(error)Invalid key"
    else:
        return "(error) ERR wrong number of arguments for 'zincrby' command "


def zcard(cmd_list, rClient):
    zset_data = RS.rDB[rClient.redisDbId].zset_data
    cmd_list.pop(0)
    if len(cmd_list) == 1:
        all_keys = get_all_keys(rClient)
        key = cmd_list[0]
        if is_valid_redis_key(key):
            check_zset_key(key, rClient)
            if key not in all_keys:
                return "(error)ERR the key is not existed"
            else:
                return "the members of " + str(key) + "\tis\t" + str(len(zset_data[key]))
        else:
            return "(error)Invalid key"
    else:
        return "(error) ERR wrong number of arguments for 'zcard' command "


# 返回特定member的序号，升序，不改变原zset
def zrank(cmd_list, rClient):
    zset_data = RS.rDB[rClient.redisDbId].zset_data
    cmd_list.pop(0)
    if len(cmd_list) == 2:
        all_keys = get_all_keys(rClient)
        key = cmd_list[0]
        if is_valid_redis_key(key):
            cmd_list.pop(0)
            check_zset_key(key, rClient)
            if key not in all_keys:
                return "(error)ERR the key is not existed"
            else:
                if cmd_list[0] not in zset_data[key]:
                    return "(error)There was no member named" + str(cmd_list[1])
                else:
                    new_dict = zsort(zset_data[key])
                    add = 0
                    for id, value in new_dict.items():
                        if id == key:
                            break
                        add += 1
                    return "the member's rank is\t" + str(add)
        return "(error)Invalid key"
    return "(error) ERR wrong number of arguments for 'zrank' command "


# 返回特定member的序号，降序，不改变原zset
def zrevrank(cmd_list, rClient):
    zset_data = RS.rDB[rClient.redisDbId].zset_data
    cmd_list.pop(0)
    if len(cmd_list) == 2:
        all_keys = get_all_keys(rClient)
        key = cmd_list[0]
        if is_valid_redis_key(key):
            cmd_list.pop(0)
            check_zset_key(key, rClient)
            if key not in all_keys:
                return "(error)ERR the key is not existed"
            else:
                if cmd_list[0] not in zset_data[key]:
                    return "(error)There was no member named" + str(cmd_list[1])
                else:
                    new_dict = zsortrever(zset_data[key])
                    add = 0
                    for id, value in new_dict.items():
                        if id == key:
                            break
                        add += 1
                    return "the member's rank is\t" + str(add)
        return "(error)Invalid key"
    return "(error) ERR wrong number of arguments for 'zrank' command "


def zunionstore(cmd_list, rClient):
    zset_data = RS.rDB[rClient.redisDbId].zset_data
    cmd_list.pop(0)
    if len(cmd_list) >= 4:
        all_keys = get_all_keys(rClient)
        desti = cmd_list[0]
        if is_valid_redis_key(desti):
            cmd_list.pop(0)
            check_zset_key(desti, rClient)
            if desti in all_keys:
                return "(error)the zset has existed"
            else:
                zset_data[desti] = {}
                num = int(cmd_list[0])
                cmd_list.pop(0)
                if num != len(cmd_list):
                    return "(error)numkeys is not equal to the keys"
                for i in range(num):
                    if i == 0:
                        if cmd_list[i] not in all_keys:
                            return "(error)the zset not exist"
                        else:
                            zset_data[desti] = zset_data[cmd_list[i]].copy()
                    else:
                        if cmd_list[i] not in all_keys:
                            return "(error)the zset not exist"
                        else:
                            zset_data[desti].update(zset_data[cmd_list[i]])
                zset_data[desti] = zsort(zset_data[desti])
                return "(integer)" + str(len(zset_data[desti]))
        else:
            "(error)Invalid key"
    return "(error) ERR wrong number of arguments for 'zunionstore' command "


def zinsertstore(cmd_list, rClient):
    zset_data = RS.rDB[rClient.redisDbId].zset_data
    cmd_list.pop(0)
    if len(cmd_list) >= 4:
        all_keys = get_all_keys(rClient)
        desti = cmd_list[0]
        if is_valid_redis_key(desti):
            cmd_list.pop(0)
            check_zset_key(desti, rClient)
            if desti in all_keys:
                return "(error)the zset has existed"
            else:
                zset_data[desti] = {}
                insert = []
                num = int(cmd_list[0])
                cmd_list.pop(0)
                if num != len(cmd_list):
                    return "(error)numkeys is not equal to the keys"
                for i in range(num):
                    if i == 0:
                        if cmd_list[i] not in all_keys:
                            return "(error)the zset not exist"
                        else:
                            zset_data[desti] = zset_data[cmd_list[i]].copy()
                            insert = zset_data[cmd_list[i]].keys()
                    else:
                        if cmd_list[i] not in all_keys:
                            return "(error)the zset not exist"
                        else:
                            zset_data[desti].update(zset_data[cmd_list[i]])
                            insert = insert & zset_data[cmd_list[i]].keys()
                zset_data[desti] = zsort(zset_data[desti])
                zset_data[desti] = {id: zset_data[desti][id] for id in insert}
                return "(integer)" + str(len(zset_data[desti]))
        else:
            "(error)Invalid key"
    return "(error) ERR wrong number of arguments for 'zinsertstore' command "


def zrangebyscore(cmd_list, rClient):
    zset_data = RS.rDB[rClient.redisDbId].zset_data
    cmd_list.pop(0)
    if len(cmd_list) == 3:
        all_keys = get_all_keys(rClient)
        key = cmd_list[0]
        if is_valid_redis_key(key):
            cmd_list.pop(0)
            check_zset_key(key, rClient)
            if key not in all_keys:
                return "(error)the zset not existed"
            else:
                min = float(cmd_list[0])
                max = float(cmd_list[1])
                result = {key: value for key, value in zset_data[key].items() if min <= value <= max}
                result = zsort(result)
                tmp = []
                i = 0
                for x, value in result.items():
                    tmp.append(f"{i + 1}){x}")
                    i += 1
                tmp = "\n".join(tmp)
                return tmp
        else:
            "(error)Invalid key"
    else:
        return "(error) ERR wrong number of arguments for 'zrangebyscore' command "


def zrevrangebyscore(cmd_list, rClient):
    zset_data = RS.rDB[rClient.redisDbId].zset_data
    cmd_list.pop(0)
    if len(cmd_list) == 3:
        all_keys = get_all_keys(rClient)
        key = cmd_list[0]
        if is_valid_redis_key(key):
            cmd_list.pop(0)
            check_zset_key(key, rClient)
            if key not in all_keys:
                return "(error)the zset not existed"
            else:
                min = float(cmd_list[0])
                max = float(cmd_list[1])
                result = {key: value for key, value in zset_data[key].items() if min <= value <= max}
                result = zsortrever(result)
                tmp = []
                i = 0
                for x, value in result.items():
                    tmp.append(f"{i + 1}){x}")
                    i += 1
                tmp = "\n".join(tmp)
                return tmp
        else:
            "(error)Invalid key"
    else:
        return "(error) ERR wrong number of arguments for 'zrevrangebyscore' command "


def remrangebyscore(cmd_list, rClient):
    zset_data = RS.rDB[rClient.redisDbId].zset_data
    cmd_list.pop(0)
    if len(cmd_list) == 3:
        all_keys = get_all_keys(rClient)
        key = cmd_list[0]
        if is_valid_redis_key(key):
            cmd_list.pop(0)
            check_zset_key(key, rClient)
            if key not in all_keys:
                return "(error)the zset not existed"
            else:
                min = float(cmd_list[0])
                max = float(cmd_list[1])
                result = {key: value for key, value in zset_data[key].items() if min > key or key > max}
                zset_data[key] = result
                return "Done,(integer)" + str(len(zset_data[key]))
        else:
            return "(error)Invalid key"
    else:
        return "(error) ERR wrong number of arguments for 'remrangebyscore' command "


def zgetall(cmd_list, rClient):
    zset_data = RS.rDB[rClient.redisDbId].zset_data
    cmd_list.pop(0)
    if len(cmd_list) == 1:
        all_keys = get_all_keys(rClient)
        key = cmd_list[0]
        if is_valid_redis_key(key):
            cmd_list.pop(0)
            check_zset_key(key, rClient)
            if key not in all_keys:
                return "(error)the zset not existed"
            else:
                tmp = []
                i = 0
                for x, value in zset_data[key].items():
                    tmp.append(f"{i + 1}){x}")
                    i += 1
                tmp = "\n".join(tmp)
                return tmp
        else:
            return "(error)Invalid key"
    else:
        return "(error) ERR wrong number of arguments for 'zgetall' command "
